﻿using FAQ_UI.window;
using java.io;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using yzrilyzr.asn1;
using yzrilyzr.crypto;
using yzrilyzr.FAQ.client;
using yzrilyzr.FAQ.constants;
using yzrilyzr.FAQ.protocol;
using yzrilyzr.FAQ.utils;
using yzrilyzr.util;
using Exception = System.Exception;
using File = System.IO.File;
using Process = System.Diagnostics.Process;

namespace FAQ_UI
{
	public class MsgEntryHolder : ViewHolder, MsgPackReceiver
	{
		public static string savedObject;
		public Grid AudioMsg;
		public MyDrawableView Avatar;
		public StackPanel BubbleContent;
		public StackPanel ReplyContent;
		public Grid BubbleMsg;
		public MyDrawableView BubbleUI;
		public MyDrawableView FileIcon;
		public StackPanel FileMsg;
		public TextBlock FileName;
		public TextBlock FileSize;
		public TextBlock FileState;
		public TextBlock ReplyMsg;
		public MyDrawableView Img;
		public TextBlock ImgBroken;
		public Grid ImgMsg;
		public Grid MsgMain;
		public TextBlock SMsg;
		public TextBlock Name;
		public MsgPack p;
		public TextBlock SendState;
		public TextBlock TextMsg;
		public ProgressBar FileProgress;
		public MyAdapterView adapterView;
		public IHasToID hasToId;
		private List<MsgPack> msg;
		private ContextMenu menu;

		static MsgEntryHolder()
		{
			savedObject = save(parse("/uitemplate/MsgEntryTemplate.xaml"));
		}

		public MsgEntryHolder(IHasToID hasToID, List<MsgPack> list, MyAdapterView adapterView)
		{
			setView(Clone(savedObject));
			ImgBroken = get<TextBlock>("ImgBroken");
			TextMsg = get<TextBlock>("TextMsg");
			Avatar = get<MyDrawableView>("Avatar");
			SendState = get<TextBlock>("SendState");
			Name = get<TextBlock>("Name");
			FileMsg = get<StackPanel>("FileMsg");
			AudioMsg = get<Grid>("AudioMsg");
			BubbleMsg = get<Grid>("BubbleMsg");
			BubbleContent = get<StackPanel>("BubbleContent");
			ReplyContent = get<StackPanel>("ReplyContent");
			Img = get<MyDrawableView>("Img");
			FileIcon = get<MyDrawableView>("FileIcon");
			ImgMsg = get<Grid>("ImgMsg");
			MsgMain = get<Grid>("MsgMain");
			BubbleUI = get<MyDrawableView>("BubbleUI");
			SMsg = get<TextBlock>("SMsg");
			FileName = get<TextBlock>("FileName");
			FileSize = get<TextBlock>("FileSize");
			FileState = get<TextBlock>("FileState");
			ReplyMsg = get<TextBlock>("ReplyMsg");
			FileProgress = get<ProgressBar>("FileProgress");
			Avatar.MouseLeftButtonUp += clickAvatar;
			MouseEvent imgEvent = new MouseEvent(Img);
			imgEvent.leftClick += clickImgMsg;
			imgEvent.leftStartDrag += startDragImgMsg;
			MouseEvent bubbleEvent = new MouseEvent(BubbleMsg);
			bubbleEvent.leftClick += clickBubble;
			bubbleEvent.leftStartDrag += startDragBubble;
			Grid main = (Grid)get();
			menu = (ContextMenu)main.FindResource("ImgContextMenu");
			getItem("Copy").Click += clickCopyr;
			getItem("SaveToSticker").Click += clickSaveToSticker;
			getItem("SaveToFile").Click += clickSaveToFile;
			getItem("Reply").Click += clickReply;
			getItem("Forward").Click += clickForward;
			getItem("Collect").Click += clickCollect;
			getItem("Recall").Click += clickRecall;
			getItem("Delete").Click += clickDelete;
			this.adapterView = adapterView;
			this.hasToId = hasToID;
			this.msg = list;
			FAQUtil.eventBus.addReceiver(this);
		}

		public void setReply(MsgPack ms)
		{
			if (msg == null || ms == null) return;
			FAQID rep = ms.getUUID("reply");
			if (UUIDs.isNull(rep))
			{
				ReplyContent.Visibility = Visibility.Collapsed;
				return;
			}
			ReplyContent.Visibility = Visibility.Visible;
			ReplyContent.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(128, 128, 128, 128));
			ReplyContent.MouseLeftButtonDown += clickReplyText;
			MsgPack ta = null;
			foreach (MsgPack c in msg)
			{
				if (c.getPkgid().equals(rep))
				{
					ta = c;
					break;
				}
			}
			if (ta != null)
			{
				ReplyMsg.Text = (string.Format("{0} {1} {2}",
												FAQUtil.getCommNameOrName(ta.getFromUser()),
												FAQUtil.getTimeStringByNow(ta.getTime()),
												ta.getStrData()));
			}
			else ReplyMsg.Text = "[消息内容已失效]";
		}

		private void clickReplyText(object sender, MouseButtonEventArgs e)
		{
			FAQID rep = p.getUUID("reply");
			if (!UUIDs.isNull(rep))
			{
				int pod = -1;
				foreach (MsgPack c in msg)
				{
					if (c.getPkgid().equals(rep)) break;
					pod++;
				}
				pod++;
				if (pod >= 0 && pod < msg.Count)
				{
					adapterView.setSelection(pod);
				}
				else Toast.Show("无法找到原消息");
			}
		}

		private void clickSaveToSticker(object sender, RoutedEventArgs e)
		{
			byte[] b = FAQUtil.client.getBigData(p);
			if (b == null || b.Length == 0)
			{
				Toast.Show("盗不了这图");
				return;
			}
			FAQUtil.client.getStickerHelper().add(b);
			FAQUtil.eventBus.asyncSend(new BinMsgPack(App.UPDATE_STICKER));
			Toast.Show("盗图成功");
		}

		private MenuItem getItem(string name)
		{
			foreach (object i in menu.Items)
			{
				if (i is MenuItem && ((MenuItem)i).Name == name) return (MenuItem)i;
			}
			return null;
		}

		private void clickDelete(object sender, RoutedEventArgs e)
		{
			FAQUtil.client.getChatRecordHelper(hasToId.getTo()).modify(p.setCmd(C.IGNORE));
			adapterView.notifyDataChanged();
		}

		private void clickRecall(object sender, RoutedEventArgs e)
		{
			FAQUtil.client.sendRecallMsg(p);
			IChatRecordHelper h = FAQUtil.client.getChatRecordHelper(hasToId.getTo());
			h.modify(p.setCmd(C.RECALL));
			adapterView.notifyDataChanged();
		}

		private void clickCollect(object sender, RoutedEventArgs e)
		{
			FAQUtil.client.saveToCollectionAndUpdate(p);
			Toast.Show("已添加到收藏");
		}

		private void clickForward(object sender, RoutedEventArgs e)
		{
		}

		private void clickReply(object sender, RoutedEventArgs e)
		{
			FAQUtil.eventBus.asyncSend(new BinMsgPack(App.SET_CHAT_REPLY).setData(p.toByte()));
		}

		private void clickCopyr(object sender, RoutedEventArgs e)
		{
			int cmd = p.getCmd();
			if (cmd == C.PIC || cmd == C.STICKER)
			{
				byte[] by = FAQUtil.client.getBigData(p);
				if (by == null || by.Length == 0)
				{
					Toast.Show("无效的图片");
					return;
				}
				BitmapImage im = FAQUtil.loadBitmap(by);
				if (im == null)
				{
					Toast.Show("无效的图片");
					return;
				}
				Clipboard.SetImage(im);
			}
			else if (cmd == C.MSG)
			{
				Clipboard.SetText(p.getStrData());
			}
			else if (cmd == C.UIDCARD)
			{
				Clipboard.SetText(p.getUUID(K.DATA) + "");
			}
			else if (cmd == C.AUDIO)
			{
				Clipboard.SetAudio(p.getData());
			}
		}

		private void clickSaveToFile(object sender, RoutedEventArgs e)
		{
			try
			{
				string path = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
				path += "/FAQ";
				DirectoryInfo pp = new DirectoryInfo(path);
				if (!pp.Exists) pp.Create();
				BufferedOutputStream o = new BufferedOutputStream(new FileOutputStream(pp.FullName + "/" + AESUtil
										.byte2LongDigest(p.getData()) + p.getTime() + ".png"));
				byte[] bb = p.getData();
				if (bb == null || bb.Length == 0)
				{
					if (p.getCmd() == C.STICKER) bb = FAQUtil.client.getBigData(p);
					else if (p.getCmd() == C.PIC) bb = FAQUtil.client.getBigData(p);
					else throw new Exception("no data");
				}
				if (bb == null) throw new Exception();
				o.write(bb);
				o.flush();
				o.close();
				Toast.Show("已保存到\"我的图片\"目录");
			}
			catch (Exception ee)
			{
				Toast.Show("保存失败");
			}
		}

		private void startDragImgMsg(object sender, MouseEventArgs e)
		{
			byte[] data = FAQUtil.client.getBigData(p);
			string tmp = Path.GetTempFileName() + "." + FAQUtil.getSuffix(data);
			File.WriteAllBytes(tmp, data);
			DataObject datao = new DataObject(DataFormats.FileDrop, new string[] { tmp });
			DragDrop.DoDragDrop(sender as UIElement, datao, DragDropEffects.Copy);
		}

		private void clickImgMsg(object sender, MouseButtonEventArgs e)
		{
			Logger.getDef().i("up");
			byte[] data = FAQUtil.client.getBigData(p);

			string temp = Path.GetTempFileName() + "." + FAQUtil.getSuffix(data);
			Util.write(temp, data);
			System.Diagnostics.Process process = Process.Start(temp);
		}

		public void clickAvatar(object sender, MouseButtonEventArgs args)
		{
			new UserProfile(p.getFromUser()).Show();
		}

		public void clickBubble(object sender, MouseButtonEventArgs args)
		{
			int cmd = p.getCmd();
			if (cmd == C.FILE)
			{
				if (p.getLong(K.PROGRESS) != p.getLong(K.LENGTH))
				{
					if (p.getFrom().equals(FAQUtil.client.getFAQID()))
						FAQUtil.eventBus.post(new BinMsgPack(App.BIGDATA_UPLOAD).put(K.DATA, p));
					else
						FAQUtil.eventBus.post(new BinMsgPack(App.BIGDATA_DOWNLOAD).put(K.DATA, p));
				}
				else
				{//上传或下载完毕
					FAQUtil.eventBus.post(new BinMsgPack(App.BIGDATA_OPEN).put(K.DATA, p)
																		  .setTo(hasToId.getTo()));
				}
			}
			else if (cmd == C.E2EE_KEY)
			{
				bool has = p.has(K.PUBKEY);
				if (!has)
				{
					FAQID toid = p.getFromUser();
					FAQUtil.client.addFriend(toid);
					FAQUtil.client.sendMyE2EEKey(toid);
				}
				else
				{
					FAQID toid = p.getFromUser();
					byte[] key = p.getByteArr(K.PUBKEY);
					string asn1;
					try
					{
						asn1 = ASN1.read(new ByteArrayInputStream(key)).toString();
					}
					catch (System.Exception e) { asn1 = "读取失败，无效的ASN.1格式"; }
					MessageBoxResult result = MessageBox.Show(asn1, System.String.Format("确定收下{0}的公钥？", FAQUtil.getCommNameOrName(toid)), MessageBoxButton.YesNo);
					if (result == MessageBoxResult.Yes)
					{
						FAQUtil.client.addFriend(toid);
						FAQUtil.client.addE2EEKey(p);
					}
				}
			}
			else if (cmd == C.UIDCARD)
			{
				FAQID uid = p.getUUID(K.DATA);
				new UserProfile(uid).Show();
			}
		}

		private void startDragBubble(object sender, MouseEventArgs e)
		{
			DataObject datao = null;
			int cmd = p.getCmd();
			if (cmd == C.FILE)
			{
				if (p.has(K.LOCALURI))

					datao = new DataObject(DataFormats.FileDrop, new string[] { p.getString(K.LOCALURI) });
			}
			else
			{
				datao = new DataObject(typeof(MsgPack), p);
			}
			if (datao != null) DragDrop.DoDragDrop(sender as UIElement, datao, DragDropEffects.Copy);
		}

		public override void set(int i, object data, IList datalist)
		{
			p = (MsgPack)data;
			get().Visibility = (p.getCmd() == C.IGNORE ? Visibility.Collapsed : Visibility.Visible);
			FAQClient client = FAQUtil.client;
			setIsFromMe(p.getFrom().equals(client.getFAQID()));
			setMsgState(p);
			setAvatar(p);
			setReply(p);
			Name.Text = FAQUtil.getCommNameOrName(p.getFromUser());
			Name.Visibility = UUIDs.isGroup(hasToId.getTo()) || UUIDs.CHATROOM.equals(hasToId.getTo()) ? Visibility.Visible : Visibility.Collapsed;
			//Index.Text = "" + i;
			//Logger.getDef().i("Index:" + get<TextBlock>("Index").Text);
			if (p.getCmd() != C.SMSG) FAQUtil.client.sendIsReadMsg(p);
			if (UUIDs.FAVOURITE.equals(hasToId.getTo()))
			{
				getItem("Collect").Visibility = Visibility.Collapsed;
				getItem("Reply").Visibility = Visibility.Collapsed;
				getItem("Recall").Visibility = Visibility.Collapsed;
			}
			if (!p.isMsg())
			{
				setSmsg("不是消息");
				return;
			}
			bool secure = p.getBoolean(K.SECURE);
			if (secure && !FAQUtil.isSafe)
			{
				setSmsg("[该消息暂不支持显示]");
				return;
			}
			int cmd = p.getCmd();
			if (cmd == C.PIC || cmd == C.STICKER)
			{
				getItem("SaveToSticker").Visibility = Visibility.Visible;
				getItem("SaveToFile").Visibility = Visibility.Visible;
			}
			else
			{
				getItem("SaveToSticker").Visibility = Visibility.Collapsed;
				getItem("SaveToFile").Visibility = Visibility.Collapsed;
			}
			if (cmd == C.MSG)
			{
				setTextMsg(p, p.getStrData());
			}
			else if (cmd == C.PIC || cmd == C.STICKER)
			{
				setImageMsg(p);
			}
			else if (cmd == C.AUDIO)
			{
				setAudioMsg(p);
			}
			else if (cmd == C.FILE || cmd == C.E2EE_KEY || cmd == C.UIDCARD)
			{
				setFileMsg(p);
			}
			else if (cmd == C.SMSG)
			{
				setSmsg(p.getStrData());
			}
			else if (cmd == C.RECALL)
			{
				setSmsg(string.Format("{0} 撤回了一条消息", FAQUtil.getCommNameOrName(p.getFromUser())));
			}
			else if (cmd == C.FLASHPIC)
			{
				if (!FAQUtil.isSafe)
				{
					setSmsg("[该消息暂不支持显示]");
					return;
				}
				else
				{
					setImageMsg(p);
				}
			}
		}

		private void setSmsg(string v)
		{
			SMsg.Visibility = Visibility.Visible;
			MsgMain.Visibility = Visibility.Collapsed;
			SMsg.Text = v;
			menu.Visibility = Visibility.Collapsed;
		}

		public void setAudioMsg(MsgPack p)
		{
			setBubble(p);
			TextMsg.Visibility = Visibility.Collapsed;
			FileMsg.Visibility = Visibility.Collapsed;
			AudioMsg.Visibility = Visibility.Visible;
		}

		public void setAvatar(MsgPack p)
		{
			Avatar.Drawable = FAQUtil.getAvatar(p.getFromUser());
		}

		public void setBubble(MsgPack p)
		{
			NinePatchDrawable image = p == null ? null : FAQUtil.getBubble(p.getFrom());
			Thickness padding;
			if (image == null)
			{
				BubbleMsg.Visibility = Visibility.Collapsed;
				padding = new Thickness(0, 0, 0, 0);
				BubbleUI.Drawable = null;
				BubbleUI.InvalidateVisual();
			}
			else
			{
				BubbleMsg.Visibility = Visibility.Visible;
				ImgMsg.Visibility = Visibility.Collapsed;
				SMsg.Visibility = Visibility.Collapsed;
				MsgMain.Visibility = Visibility.Visible;
				bool mirror = p.getFrom().equals(FAQUtil.client.getFAQID());
				padding = image.getPadding(70, mirror);
				image.Mirror = mirror;
				BubbleUI.Drawable = image;
				BubbleUI.InvalidateVisual();
			}
			BubbleContent.Margin = padding;
		}

		public void setFileMsg(MsgPack p)
		{
			int cmd = p.getCmd();
			if (cmd == C.FILE)
			{
				ProgressMonitor mon = (ProgressMonitor)ClientUtils.bigdata.get(p);
				long prog = p.getProgress();
				long len = p.getLength();
				bool isUpload = p.getFrom().equals(FAQUtil.client.getFAQID());
				bool isDone = prog == p.getLength();
				bool isPending = prog == 0;
				bool running = mon != null;
				bool paused = mon != null && mon.getState() == ProgressMonitor.State.PAUSED;
				string state = "";
				string doneStr = (isUpload ? "上传成功" : "下载成功");
				string runningStr = (isUpload ? "上传中" : "下载中");
				string pausedStr = (isUpload ? "上传暂停" : "下载暂停");
				string pendingStr = (isUpload ? "未上传" : "未下载");
				string failStr = (isUpload ? "上传失败" : "下载失败");
				if (isDone) state = doneStr;
				else if (paused) state = pausedStr;
				else if (running) state = runningStr;
				else if (isPending) state = pendingStr;
				else state = failStr;
				if (len == 0) len = 1;
				int rat = (int)(1000L * prog / len);
				FileProgress.Maximum = 1000;
				FileProgress.Value = (rat);
				FileProgress.Visibility = running ? Visibility.Visible : Visibility.Collapsed;

				setFileMsg(FAQUtil.imageCache.get("FileIcon" + p.getPkgid(), new Loader<Drawable>(() =>
				{
					BitmapImage im = FAQUtil.loadBitmap(p.getByteArr(K.ICON));
					return im == null ? FAQUtil.getDrawableRes("/icon.ico") : new BitmapDrawable(im);
				})), p.getString(K.NAME), FileUtil.getFileSizeBinStr(p.getLength()), state);
			}
			else if (cmd == C.E2EE_KEY)
			{
				FileProgress.Visibility = Visibility.Collapsed;
				FAQID from = p.getFromUser();
				bool has = p.has(K.PUBKEY);
				string des = has ? string.Format("来自{0}的公共密钥（UID：{1}）", FAQUtil.getCommNameOrName(from), from
					.toInt()) : "需要公共密钥";
				string des2 = has ? "点击这里收下公钥" : "点击这里发送自己的公钥";
				setFileMsg(FAQUtil.getAvatar(from), des, des2, "");
			}
			else if (cmd == C.UIDCARD)
			{
				FileProgress.Visibility = Visibility.Collapsed;
				FAQID uid = p.getUUID(K.DATA);
				string name = FAQClient.getPubUsrDB().contV(D.NAMES, uid) ? FAQUtil.getCommNameOrName(uid) : p.getString(K.NAME);
				string des = FAQClient.getPubUsrDB().contV(D.DESCRIPTIONS, uid) ? FAQUtil.getDescription(uid) : p.getString(K.DESCRIPTION);
				setFileMsg(FAQUtil.getAvatar(uid), name, des, "UID:" + uid.toInt());
			}
		}

		public void setFileMsg(Drawable icon, string name, string size, string state)
		{
			setBubble(p);
			TextMsg.Visibility = Visibility.Collapsed;
			FileMsg.Visibility = Visibility.Visible;
			AudioMsg.Visibility = Visibility.Collapsed;
			FileName.Text = name;
			FileSize.Text = size;
			FileState.Text = state;
			FileIcon.Drawable = icon;
		}

		public void setImageMsg(MsgPack p)
		{
			setBubble(null);
			SMsg.Visibility = Visibility.Collapsed;
			MsgMain.Visibility = Visibility.Visible;
			Drawable src = FAQUtil.getMsgPic(p);
			Img.Drawable = src;
			Img.Visibility = src == null ? Visibility.Collapsed : Visibility.Visible;
			double max = p.getCmd() == C.STICKER ? 150 : 300;
			Img.MaxWidth = max;
			Img.MaxHeight = max;
			ImgBroken.Visibility = src == null ? Visibility.Visible : Visibility.Collapsed;
			ImgMsg.Visibility = Visibility.Visible;
		}

		public void setIsFromMe(bool isFromMe)
		{
			HorizontalAlignment a = isFromMe ? HorizontalAlignment.Right : HorizontalAlignment.Left;
			Avatar.HorizontalAlignment = a;
			SendState.HorizontalAlignment = a;
			Name.HorizontalAlignment = a;
			BubbleMsg.HorizontalAlignment = a;
			ImgMsg.HorizontalAlignment = a;
			getItem("Recall").Visibility = isFromMe ? Visibility.Visible : Visibility.Collapsed;
		}

		public void setMsgState(MsgPack p)
		{
			string[][] states = {
				new string[] { "",""},
				new string[] { "\ue000","消息仅存在于本地\n可能的原因：\n1.您未在线\n2.您没有对方公钥\n3.服务器找不到对应的用户\n4.对方为本地服务号" },
				new string[] { "\ue001","消息已缓存至服务器，有时间限制\n可能的原因：对方未在线" },
				new string[] { "\ue002","对方成功地接收了消息，但暂未阅读" },
				new string[] { "\ue003","对方已经阅读了该消息" },
				new string[] { "\ue004" , "发送者校验失败\n可能的原因：\n1.您没有对方公钥\n2.此消息为第三方伪造\n3.对方为本地服务号，无需校验" },
				new string[] { "\ue005" , "对方成功地接收了消息，但无法阅读\n可能的原因：\n1.对方使用的的公钥失效\n2.您拥有的对方公钥是无效的\n3.消息传输过程存在劫持" }
			};
			string[] s = states[0];
			Flag f = new Flag(p.getMsgState());
			if (FAQUtil.client.getFAQID().equals(p.getFrom()))
			{
				if (f.has(MsgState.LOCAL_ONLY)) s = states[1];
				if (f.has(MsgState.SERVER_CACHE)) s = states[2];
				if (f.has(MsgState.RECEIVED)) s = states[3];
				if (f.has(MsgState.READ)) s = states[4];
				if (f.has(MsgState.READ_FAIL)) s = states[6];
			}
			else if (!f.has(MsgState.VERIFIED) && p.getCmd() != C.E2EE_KEY && !UUIDs.isReserved(p.getFromUser())) s = states[5];
			SendState.Text = s[0];
			SendState.ToolTip = s[1];
		}

		public void setTextMsg(MsgPack p, string s)
		{
			setBubble(p);
			TextMsg.Text = s;
			TextMsg.Visibility = Visibility.Visible;
			FileMsg.Visibility = Visibility.Collapsed;
			AudioMsg.Visibility = Visibility.Collapsed;
		}

		public override ViewHolder newInstance()
		{
			return new MsgEntryHolder(hasToId, msg, adapterView);
		}

		void MsgPackReceiver.receive(MsgPackTransfer mpt, MsgPack msgPack)
		{
			int cmd = msgPack.getCmd();
			if (cmd == App.BIGDATA_MONITOR_UPDATE)
			{
				MsgPack mFilePack = (MsgPack)msgPack.get(K.DATA);
				if (p == null || mFilePack == null) return;
				if (p.getPkgid().equals(mFilePack.getPkgid()))
				{
					App.Current.Dispatcher.Invoke(new Action(() =>
					{
						p.setProgress(mFilePack.getProgress());
						setFileMsg(p);
					}));
				}
			}
		}
	}
}